﻿using System;
using System.Windows;
using System.Windows.Media;

namespace MvvmFx.Common.Helpers
{
    public static class WpfVisualTreeHelper
    {
        /// <summary>
        /// Finds the first occurence of the visual child object by the specified type.
        /// </summary>
        /// <typeparam name="T">The type of the object to find.</typeparam>
        /// <param name="obj">The dependency object that you wish to find the child object type from.</param>
        /// <returns>The first occurence of the visual child object found.</returns>
        public static T FindVisualObjectByTypeVisualHelper<T>(DependencyObject obj) where T : DependencyObject
        {
            int count = VisualTreeHelper.GetChildrenCount(obj);
            for (int i = 0; i < count; i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                if (child != null && child is T)
                {
                    return (T)child;
                }

                T childOfChild = FindVisualObjectByTypeVisualHelper<T>(child);
                if (childOfChild != null)
                {
                    return childOfChild;
                }
            }
            return null;
        }

        public static T GetObjectOfTypeInVisualTree<T>(DependencyObject dpob) where T : DependencyObject
        {
            return GetObjectOfTypeInVisualTree<T>(dpob, string.Empty);
        }

        public static T GetObjectOfTypeInVisualTree<T>(DependencyObject dpob, string name) where T : DependencyObject
        {
            int count = VisualTreeHelper.GetChildrenCount(dpob);

            for (int i = 0; i < count; i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(dpob, i);

                T childAsT = child as T;

                if (childAsT != null)
                {
                    if ((name != string.Empty && (childAsT as FrameworkElement).Name == name)
                        || name == string.Empty)
                    {
                        return childAsT;
                    }
                }

                childAsT = GetObjectOfTypeInVisualTree<T>(child, name);

                if (childAsT != null)
                {
                    if ((name != string.Empty && (childAsT as FrameworkElement).Name == name)
                        || name == string.Empty)
                    {
                        return childAsT;
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// Finds a parent of a given item on the visual tree.
        /// </summary>
        /// <typeparam name="T">The type of the queried item.</typeparam>
        /// <param name="child">A direct or indirect child of the
        /// queried item.</param>
        /// <returns>The first parent item that matches the submitted
        /// type parameter. If not matching item can be found, a null
        /// reference is being returned.</returns>
        public static T TryFindParent<T>(DependencyObject child)
            where T : DependencyObject
        {
            //get parent item
            DependencyObject parentObject;
            try
            {
                parentObject = VisualTreeHelper.GetParent(child);
            }
            catch (InvalidOperationException)
            {
                //we’ve reached the end of the tree, if a non-visual object is found
                return null;
            }

            //we’ve reached the end of the tree
            if (parentObject == null)
                return null;

            //check if the parent matches the type we’re looking for
            T parent = parentObject as T;
            if (parent != null)
            {
                return parent;
            }
            else
            {
                //use recursion to proceed with next level
                return TryFindParent<T>(parentObject);
            }
        }

        public static T TryFindParent<T>(FrameworkElement child, string parentName)
            where T : FrameworkElement
        {
            //get parent item
            FrameworkElement parentObject = VisualTreeHelper.GetParent(child) as FrameworkElement;

            //we’ve reached the end of the tree
            if (parentObject == null || !(parentObject is T))
            {
                return null;
            }

            T parent = parentObject as T;

            if (string.IsNullOrEmpty(parentName) || parentObject.Name == parentName)
            {
                return parent;
            }
            else
            {
                //use recursion to proceed with next level
                return TryFindParent<T>(parentObject);
            }
        }
    }
}